from typing import List, Union

from sqlalchemy import UniqueConstraint, and_

from tools import db
from utils.enums import IntegerChoices, TextChoices

from . import BaseModel


class PresetRoleChoices(TextChoices):
    admin = "admin", "管理员"
    teacher = "teacher", "教师"
    student = "student", "学生"
    rb_referee = "rb_referee", "红蓝裁判"
    rb_pilot = "rb_pilot", "红蓝导调"
    audience = "audience", "观众"


class Role(db.Model, BaseModel):
    """角色表"""

    __tablename__ = "api_roles"

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(
        db.String(64), unique=True, nullable=False, index=True, comment="角色名称"
    )
    show_name = db.Column(db.String(64), unique=True, nullable=False, comment="角色展示名称")
    description = db.Column(db.String(64), default="", comment="角色描述")

    is_system = db.Column(db.Boolean, index=True, default=False, comment="是否是系统角色")
    is_enabled = db.Column(db.Boolean, index=True, default=True, comment="是否启用")

    @property
    def users(self):
        from app.models.userModel import User

        user_list: List[User] = (
            db.session.query(User)
            .join(
                UserRole, and_(UserRole.role_id == self.id, UserRole.user_id == User.id)
            )
            .all()
        )
        return user_list

    @users.setter
    def users(self, user_list: Union[List, None]):
        db.session.query(UserRole).filter(UserRole.user_id == self.id).delete()

        if user_list is None:
            return
        for user in user_list:
            db.session.add(UserRole(user_id=user.id, role_id=self.id))

    def to_dict(self):
        return {
            "id": self.id,
            "name": self.name,
            "show_name": self.show_name,
            "description": self.description,
            "is_system": self.is_system,
            "is_enabled": self.is_enabled,
        }


_actions = ("GET", "POST", "PATCH", "DELETE")


class UserRole(db.Model, BaseModel):
    """用户角色表"""

    __tablename__ = "api_user_roles_map"
    __table_args__ = (UniqueConstraint("user_id", "role_id", name="idx_user_role"),)

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_id = db.Column(db.Integer, nullable=False, index=True, comment="用户id")
    role_id = db.Column(db.Integer, nullable=False, index=True, comment="角色id")


class Permission(db.Model, BaseModel):
    """权限资源表
    !!!--->>>: 权限初始化时填充该表，生成后为静态表, 新增路由后应该重新生成该表
    """

    class TypeChoices(IntegerChoices):
        blueprint = 0, "蓝图"
        rule = 1, "路由"

    __tablename__ = "api_permissions"
    __table_args__ = (UniqueConstraint("name", "path", name="idx_name_with_path"),)

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    name = db.Column(
        db.String(255),
        nullable=False,
        index=True,
        comment="资源名称",
        doc="为蓝图时存储完整蓝图名称blue.real_name，为路由时为rule.endpoint, "
        "通过app.blueprints 或 app.url_map.iter_rules(endpoint)可以快速获取对应实例进行操作",
    )
    path = db.Column(db.String(255), nullable=False, default="", comment="资源路径")
    type = db.Column(
        db.Integer, nullable=False, comment="资源类型，0为蓝图/菜单，1为路由", doc="限制为TypeChoices内容"
    )
    allow_no_login = db.Column(
        db.Integer,
        nullable=False,
        default=0,
        comment="是否免登陆",
        doc="仅限settings.ALLOW_NO_LOGIN中",
    )
    is_white = db.Column(db.Boolean, nullable=False, default=0, comment="是否是白名单路由")
    is_menu = db.Column(db.Boolean, nullable=False, default=0, comment="是否是菜单")
    parent_id = db.Column(
        db.Integer, db.ForeignKey("api_permissions.id"), nullable=True, comment="父级权限id"
    )
    parent = db.relationship("Permission", remote_side=[id])
    children = db.relationship("Permission", remote_side=[parent_id])


class FlagChoices(IntegerChoices):
    READ = 1 << 0, "查询"  # GET
    CREATE = 1 << 1, "添加"  # POST
    UPDATE = 1 << 2, "修改"  # PUT/PATCH
    DELETE = 1 << 3, "删除"  # DELETE


METHOD_TO_FLAG = {
    "GET": FlagChoices.READ,
    "POST": FlagChoices.CREATE,
    "PUT": FlagChoices.UPDATE,
    "PATCH": FlagChoices.UPDATE,
    "DELETE": FlagChoices.DELETE,
}


class RolePermission(db.Model, BaseModel):
    """角色权限表"""

    __tablename__ = "api_role_permissions_map"
    __table_args__ = (
        UniqueConstraint("role_id", "permission_id", name="idx_role_permission"),
    )

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    role_id = db.Column(db.Integer, nullable=False, index=True, comment="角色id")
    permission_id = db.Column(db.Integer, nullable=False, index=True, comment="权限id")
    allow_edit = db.Column(
        db.Boolean, nullable=False, default=True, comment="该关系是否允许修改"
    )
    flag = db.Column(db.Integer, nullable=True, comment="权限位标识符", doc="见 FlagChoices")


class FilterChoices(TextChoices):
    READ = "read", "查询"  # GET
    CREATE = "create", "添加"  # POST
    UPDATE = "update", "修改"  # PUT/PATCH
    DELETE = "delete", "删除"  # DELETE


METHOD_TO_FILTER = {
    "GET": FilterChoices.READ,
    "POST": FilterChoices.CREATE,
    "PUT": FilterChoices.UPDATE,
    "PATCH": FilterChoices.UPDATE,
    "DELETE": FilterChoices.DELETE,
}


class RolePolicyFilter(db.Model, BaseModel):
    """用户权限策略表
    用户授权某角色后，角色拥有的权限经过该表过滤，显示最终的用户权限
    策略不随着角色变化而变化，当某时刻生成禁止访问某用户访问某资源的策略时，该用户后续角色调整均不会影响该策略
    若需要对策略进行修改，需要手动进行调整
    """

    __tablename__ = "api_user_policy"
    __table_args__ = (
        UniqueConstraint("user_id", "permission_id", name="idx_user_permission"),
    )

    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    user_id = db.Column(db.Integer, nullable=False, index=True, comment="用户id")
    permission_id = db.Column(
        db.Integer,
        nullable=False,
        index=True,
        comment="资源权限id",
        doc="主要针对rule添加策略，对用户添加某个蓝图访问策略时，实际增加该蓝图下所有rule访问权限",
    )
    filter = db.Column(
        db.String(10), nullable=True, comment="资源过滤目标", doc="见 OperatorChoices"
    )
    strategy = db.Column(
        db.Enum(*["allow", "disable"]),
        nullable=False,
        comment="过滤策略，allow 允许，disable 禁止",
    )
